// JavaScript Document



/*
  PureMVC Javascript Utility - StateMachine
  Copyright (c) 2008 Leo Milmanda, Neil Manuell, Cliff Hall
  Your reuse is governed by the Creative Commons Attribution 3.0 License
 */


/**
 * Creates and registers a StateMachine described in XML.
 * 
 * <P>
 * This allows reconfiguration of the StateMachine 
 * without changing any code, as well as making it 
 * easier than creating all the <code>State</code> 
 * instances and registering them with the 
 * <code>StateMachine</code> at startup time.
 * 
 * @ see State
 * @ see StateMachine
 */
function class_org_puremvc_js_utilities_statemachine_FSMInjector() // extends Notifier
{
	
	Objs.register("org.puremvc.as3.utilities.statemachine.StateMachine",StateMachine);
	
	/**
	 * Constructor.
	 */
	function FSMInjector( fsm ) //fsm:XMLDomDocument
	{
		this.fsm = fsm;
	}
	
	
	
	Objs.extend(FSMInjector,Notifier);
	var o = FSMInjector.prototype;
	

	
	/**
	 * Inject the <code>StateMachine</code> into the PureMVC apparatus.
	 * <P>
	 * Creates the <code>StateMachine</code> instance, registers all the states
	 * and registers the <code>StateMachine</code> with the <code>IFacade</code>.
	 */
	o.inject = function() //:void
	{
		// Create the StateMachine
		var stateMachine = new StateMachine();
		
		// Register all the states with the StateMachine
		for ( var i in states )
		{ 
			var state = states[i];
			stateMachine.registerState( state, isInitial( state.name ) );
		}                
		
		// Register the StateMachine with the facade
		this._facade.registerMediator( stateMachine );
	}

	
	/**
	 * Get the state definitions.
	 * <P>
	 * Creates and returns the array of State objects 
	 * from the FSM on first call, subsequently returns
	 * the existing array.</P>
	 */
	o.getStates = function() //:Array
	{
		if (this.stateList == null) {
			this.stateList = [];
			
			
			var stateDefs = fsm.getElementsByTagName("state");
			
			if(stateDefs == null || stateDefs.length == 0){
				stateDefs = fsm.getElementsByTagName("STATE");
			}
			
			var i;
			for ( i = 0 ; i< stateDefs.length; i++)
			{
				var stateDef = stateDefs[i];
				var state = this.createState( stateDef );
				stateList.push(state);
			}
			
		} 
		return stateList;
	}

	/**
	 * Creates a <code>State</code> instance from its XML definition.
	  */
	o.createState = function( stateDef ) //:State
	{
		var State = Objs.load("org.puremvc.js.utilities.statemachine.State");
		
		// Create State object
		var name 		= stateDef.attributes.getNamedItem("name").nodeValue;    //.attr("name");
		var exiting 	= stateDef.attributes.getNamedItem("exiting").nodeValue; //.attr("exiting");
		var entering 	= stateDef.attributes.getNamedItem("entering").nodeValue; //.attr("entering");
		var changed 	= stateDef.attributes.getNamedItem("changed").nodeValue; //.attr("changed");
		var state 		= new State( name, entering, exiting, changed );
		
		// Create transitions
		var transitions = stateDef.getElementsByTagName("transition"); //stateDef..transition as XMLList;
		var i; 
		for ( i = 0; i<transitions.length; i++)
		{
			var transDef = transitions[i];
			var action = transDef.attributes.getNamedItem("action").nodeValue;
			var target = transDef.attributes.getNamedItem("target").nodeValue;
			state.defineTrans( action , target );
		}
			
		return state;
	}

	/**
	 * Is the given state the initial state?
	 */
	o.isInitial = function( stateName ) //:Boolean
	{
		if(this._initialStateName == null)
		{
			this._initialStateName = fsm.attributes.getNamedItem("initial").nodeValue;
		}
		
		return (stateName == this._initialStateName);
	}
	
	//The Initial State Name
	o._initialStateName = null;
	
	// The XML FSM definition
	o.fsm = null; //:XML;
	
	// The List of State objects
	o.stateList = null; //:Array;
}





/*
  PureMVC AS3 Utility - StateMachine
  Copyright (c) 2008 Leandro Milmanda, Neil Manuell, Cliff Hall
  Your reuse is governed by the Creative Commons Attribution 3.0 License
 */
function class_org_puremvc_js_utilities_statemachine_State ()  
{
	Objs.register("org.puremvc.js.utilities.statemachine.State",State);
	
	/**
	 * Constructor.
	 * 
	 * @param id the id of the state
	 * @param entering an optional notification name to be sent when entering this state
	 * @param exiting an optional notification name to be sent when exiting this state
	 * @param changed an optional notification name to be sent when fully transitioned to this state
	 */
	function State( name, entering, exiting, changed )
	{
		if(Objs.extending) return;
		this.name = name;
		if ( entering !== null ) { this.entering = entering; }
		if ( exiting  !== null ) { this.exiting  = exiting; }
		if ( changed  !== null ) { this.changed  = changed; }
	}
		
	var o = State.prototype;
	// The state name
    o.name = null;
    // The notification to dispatch when entering the state
	o.entering = null;
    // The notification to dispatch when exiting the state
    o.exiting = null;
    // The notification to dispatch when the state has actually changed
    o.changed = null;
    /**
     *  Transition map of actions to target states
     */ 
     o.transitions = new Object();
	 

	/** 
	 * Define a transition. 
	 * 
	 * @param action the name of the StateMachine.ACTION Notification type.
	 * @param target the name of the target state to transition to.
	 */
	o.defineTrans = function( action, target )
	{
		if ( getTarget( action ) !== null ) return;    
		this.transitions[ action ] = target;
	}

	/** 
	 * Remove a previously defined transition.
	 */
	o.removeTrans = function( action )
	{
		this.transitions[ action ] = null;    
	}    
	
	/**
	 * Get the target state name for a given action.
	 */
	o.getTarget = function( action )
	{
		return this.transitions[ action ];
	}
        
}





/*
  PureMVC AS3 Utility - StateMachine
  Copyright (c) 2008 Leandro Milmanda, Neil Manuell, Cliff Hall
  Your reuse is governed by the Creative Commons Attribution 3.0 License
 */
function class_org_puremvc_js_utilities_statemachine_StateMachine()
{
	Objs.register("org.puremvc.as3.utilities.statemachine.StateMachine",StateMachine);
	
	var NAME = "StateMachine";

    /**
     * A Finite State Machine implimentation.
     * <P>
     * Handles regisistration and removal of state definitions, 
     * which include optional entry and exit commands for each 
     * state.</P>
     */
    function StateMachine() 
    {
		Mediator.apply(this,NAME);
		if(Objs.extending) return;
		
	}
	
	Objs.extend(StateMachine,Mediator);
	Objs.implement(StateMachine,IMediator);
	Objs.implement(StateMachine,INotifier);
	
	var o = StateMachine.prototype;
	/**
	* Action Notification name. 
	*/ 
	o.ACTION = NAME+"/notes/action" ;
	
	/**
	*  Changed Notification name  
	*/ 
	o.CHANGED = NAME + "/notes/changed";
	
	/**
	*  Cancel Notification name  
	*/ 
	o.CANCEL = NAME+"/notes/cancel";
	
	o.onRegister = function ()
	{
		if ( this.initial ) 
		{
			this.transitionTo( this.initial, null );   
		}
	}
        
	/**
	 * Registers the entry and exit commands for a given state.
	 * 
	 * @param state the state to which to register the above commands
	 * @param initial boolean telling if this is the initial state of the system
	 */
	o.registerState = function( state, isInitial )
	{
		if ( state == null || this.states[ state.name ] != null ) return;
		this.states[ state.name ] = state;
		if ( isInitial ) this.initial = state; 
	}
	
	/**
	 * Remove a state mapping. 
	 * <P>
	 * Removes the entry and exit commands for a given state 
	 * as well as the state mapping itself.</P>
	 * 
	 * @param state
	 */
	o.removeState = function( stateName )
	{
		var state = this.states[ stateName ];
		if ( state == null ) return;
		this.states[ stateName ] = null;
	}
        
	/**
	 * Transitions to the given state from the current state.
	 * <P>
	 * Sends the <code>exiting</code> notification for the current state 
	 * followed by the <code>entering</code> notification for the new state.
	 * Once finally transitioned to the new state, the <code>changed</code> 
	 * notification for the new state is sent.</P>
	 * <P>
	 * If a data parameter is provided, it is included as the body of all
	 * three state-specific transition notes.</P>
	 * <P>
	 * Finally, when all the state-specific transition notes have been
	 * sent, a <code>StateMachine.CHANGED</code> note is sent, with the
	 * new <code>State</code> object as the <code>body</code> and the name of the 
	 * new state in the <code>type</code>.
	 * 
	 * @param nextState the next State to transition to.
	 * @param data is the optional Object that was sent in the <code>StateMachine.ACTION</code> notification body
	 */
	o.transitionTo = function( nextState, data )
	{
		// Going nowhere?
		if ( nextState == null ) return;
		
		// Clear the cancel flag
		this.canceled = false;
			
		// Exit the current State 
		if ( this.getCrrentState() !== null && this.getCrrentState().exiting ) 
		{ 
			this.sendNotification( this.getCrrentState().exiting, data, nextState.name ); 
		}
		
		// Check to see whether the transition has been canceled
		if ( this.canceled ) {
			this.canceled = false;
			return;
		}
		
		// Enter the next State 
		if ( nextState.entering ) 
		{
			this.sendNotification( nextState.entering, data );
		}
		
		this.setCurrentState( nextState ) ;
		
		// Send the notification configured to be sent when this specific state becomes current 
		if ( nextState.changed ) 
		{
			this.sendNotification( this.getCurrentState().changed, data );
		}

		// Notify the app generally that the state changed and what the new state is 
		this.sendNotification( this.CHANGED, this.getCurrentState(), this.getCurrentState().name );
	}
	
	/**
	 * Notification interests for the StateMachine.
	 */
	o.listNotificationInterests = function()
	{
		return [     this.ACTION,
					this.CANCEL    ];
	}
	
	/**
	 * Handle notifications the <code>StateMachine</code> is interested in.
	 * <P>
	 * <code>StateMachine.ACTION</code>: Triggers the transition to a new state.<BR>
	 * <code>StateMachine.CANCEL</code>: Cancels the transition if sent in response to the exiting note for the current state.<BR>
	 */
	o.handleNotification = function ( note )
	{
		switch( note.getName() )
		{
			case this.ACTION:
				var action = note.getType();
				var target = this.getCurrentState().getTarget( action );
				var newState = this.states[ target ];
				if ( newState !== null)  //undefined
				{	
					this.transitionTo( newState, note.getBody() ); 
				}
				break;
				
			case this.CANCEL:
				this.canceled = true;
				break;
		}
	}
	
	/**
	 * Get the current state.
	 *  
	 * @return a State defining the machine's current state
	 */
	o.getCurrentState = function()
	{
		return this.viewComponent;  //as State;
	}
	
	/**
	 * Set the current state.
	 */
	o.setCurrentState = function( state ) 
	{
		this.viewComponent = state;
	}
	
	/**
	 * Map of States objects by name.
	 */
	o.states = new Object();
	
	/**
	 * The initial state of the FSM.
	 */
	o.initial = null;
	
	/**
	 * The transition has been canceled.
	 */
	o.canceled = false;

}




















function class_org_puremvc_js_utilities_statemachine_FsmParser()
{
	
	Objs.register("org.puremvc.js.utilities.statemachine.FsmParser",FsmParser);

	function FsmParser(){
		
	}
	
	var o = FsmParser.prototype;
	
	o.status = "new";
	o.error = null;
	o.xmldoc = null;
	
	o.loadFromUrl = function(url)
	{
		var xhttp = null;
	
		if (window.XMLHttpRequest)
		{
			xhttp=new window.XMLHttpRequest();
		}
		else
		{
			xhttp=new ActiveXObject("Microsoft.XMLHTTP");
		}
		xhttp.open("GET",url,false);
		xhttp.send("");
		
		var txt = xhttp.responseText;
		
		this.xmldoc = this.loadFromString(txt); 
	
		return this.xmldoc; 
	}
	
	o.loadFromString = function(txt)
	{
		var parser = null;
		if (window.DOMParser)
		{
			try {
				parser=new DOMParser();
				txt = txt.replace(/>\s*</g,"><");
				this.xmldoc = parser.parseFromString(txt,"text/xml");
				
				if (this.xmldoc.documentElement.nodeName=="parsererror")
				{
					this.status = "error";
					this.error = xmldoc.documentElement.childNodes[0].nodeValue;
				}
			} catch (e) {
				this.status = "error";
				this.error = e.message;
			}
		}
		else // Internet Explorer
		{
			try {
				this.xmldoc=new ActiveXObject("Microsoft.XMLDOM");
				this.xmldoc.async="false";
				txt = txt.replace(/>\s*</g,"><");
				this.xmldoc.loadXML(txt);
				if (this.xmldoc.parseError.errorCode != 0)
				{
					this.status = "error";
					this.error = + this.xmldoc.parseError.errorCode + " : " + this.xmldoc.parseError.reason + " at [" + this.xmldoc.parseError.line + "] " + this.xmldoc.parseError.srcText ; 
				}
			} catch (e) {
				this.status = "error";
				this.error = e.message;
			}			
		}
		
		if(this.status != 'error' && !this.validate())
		{
			this.status = "error";
		}
		
		return this.xmldoc;
	}
	
	o.validate = function()
	{
		//
		if(this.xmldoc == null)
		{
			this.error = "xmldoc is null";
			return false; 
		}
		
		
		var n = this.xmldoc.childNodes[0];
		if(n.nodeName.toLowerCase() == "xml")
		{
			n = this.xmldoc.childNodes[1];
		}
		
		//
		if(n == null)
		{
			this.error = "xmldoc has no childs";
			return false;		
		} // else { printf(this.xmldoc.firstChild); }
		
		if(n.nodeName.toLowerCase() != "fsm")
		{
			this.error = "xmldoc root node is node called fsm";
			return false;		
		} // else { printf(n.nodeName); }
		
		var firstn = n.attributes.getNamedItem("initial").nodeValue;
		if(firstn == null || firstn == undefined || firstn == "")
		{
			this.error = "fsm node has no initial attribute";
			return false;	
		} // else { printf( firstn ); }

		if(!n.firstChild == null){
			this.error = "fsm node has no childs";
			return false; 
		} //  else { printf(n.firstChild); }
		
		var n1 = n.childNodes.item(0);
		
		//printf(n1);
		if(n1.nodeName.toLowerCase() !== "state"){
			this.error = "fsm node has no state nodes";
			return false;
		}
		
		this.status = "valid";
		return true; 
		
	}
}



